package com.zhu.instructions.references;

import com.zhu.instructions.base.Index16Instruction;
import com.zhu.rtda.Frame;
import com.zhu.rtda.OperandStack;
import com.zhu.rtda.Slot;
import com.zhu.rtda.heap.Field;
import com.zhu.rtda.heap.InstanceKlass;
import com.zhu.rtda.heap.constant.FieldRef;
import com.zhu.rtda.heap.constant.RuntimeConstantPool;

/**
 * GETSTATIC 指令：获取指定类的静态字段值，并将其压入操作数栈。
 *
 * <p>操作码：0xB2<br>
 * 格式：getstatic indexbyte1 indexbyte2
 * getstatic 后面跟两个字节，拼成一个常量池索引，表示“要取哪个类的哪个静态字段”
 * 操作数：index → 指向运行时常量池中的 FieldRef
 *
 * <p>执行步骤：
 * <ol>
 *   <li>从当前方法的类常量池中解析出 FieldRef</li>
 *   <li>解析（或确保已解析）目标字段</li>
 *   <li>检查字段是否为 static（否则抛 IncompatibleClassChangeError）</li>
 *   <li>【TODO】初始化字段所属类（原书未实现，此处暂略）</li>
 *   <li>根据字段描述符，从类的 staticVars 中读取值并压栈</li>
 * </ol>
 *
 * <p>注意：不触发类初始化（<clinit>），但 JVMS 要求某些情况下必须初始化。
 * 教学实现通常将类初始化延迟到首次主动使用（如 new, invokestatic 等）。
 */
public class GET_STATIC extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        // 1. 获取当前方法所属的类及其运行时常量池
        InstanceKlass currentClass = frame.getMethod().getClazz();
        RuntimeConstantPool cp = currentClass.getConstantPool();

        // 2. 解析 FieldRef（假设已实现懒解析）  index就是你要get的静态字段在常量池中字段符号引用的索引 fieldRef就是你要加载的静态字段的符号引用对象
        FieldRef fieldRef = (FieldRef) cp.getConstant(this.index);
        //这一步之后 该fieldRef对象的class和field指针都被设置上了
        Field field = fieldRef.resolvedField();

        // 3. 验证字段是否为 static
        if (!field.isStatic()) {
            throw new IncompatibleClassChangeError("Field is not static: " + field.getName());
        }

        // 4. 【扩展点】应在此处初始化 field 所属类（调用 Class.init()）
        //原书标记为 "todo: init class"，教学实现常省略

        // 5. 获取字段所属类的静态变量存储区
        InstanceKlass fieldClass = field.getClazz();
        Slot[] staticVars = fieldClass.getStaticVars();
        int slotId = field.getSlotId();

        // 6. 根据字段类型从 staticVars 读取值并压入操作数栈
        OperandStack stack = frame.getOperandStack();
        String descriptor = field.getDescriptor();

        switch (descriptor.charAt(0)) {
            case 'Z': // boolean
            case 'B': // byte
            case 'C': // char
            case 'S': // short
            case 'I': // int
                stack.pushInt(staticVars[slotId].getNum());
                break;
            case 'F': // float
                stack.pushFloat(Float.intBitsToFloat(staticVars[slotId].getNum()));
                break;
            case 'J': // long (occupies two slots)
                int low = staticVars[slotId].getNum();
                int high = staticVars[slotId + 1].getNum();
                long longVal = ((long) high << 32) | (low & 0xFFFFFFFFL);
                stack.pushLong(longVal);
                break;
            case 'D': // double
                low = staticVars[slotId].getNum();
                high = staticVars[slotId + 1].getNum();
                long doubleBits = ((long) high << 32) | (low & 0xFFFFFFFFL);
                stack.pushDouble(Double.longBitsToDouble(doubleBits));
                break;
            case 'L': // reference (object)
            case '[': // reference (array)
                stack.pushRef(staticVars[slotId].getRef());
                break;
            default:
                throw new RuntimeException("Unsupported field type: " + descriptor);
        }
    }
}
